/* robot.h -- class declarations for robot-related things
 * robot.h is part of Robodrome
 *
 * Copyright (c) 2009 Alex French
 * Robodrome is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * Robodrome is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#pragma once
#include <gtkmm.h>
#include <vector>
#include "label.h"
#define RADIUS 10
#define RADIUSSQUARED 100

enum registers { X = 23, Y = 24, AIM = 26, BOTTOM, BULLET, CHANNEL, CHRONON, COLLISION, DAMAGE, DOPPLER, ENERGY, FIRE, FRIEND, HISTORY, HELLBORE, ID, KILLS, LEFT, LOOK, MINE, MISSILE, MOVEX, MOVEY, NUKE, PROBE, RADAR, RANDOM, RANGE, RIGHT, ROBOTS, SCAN, SHIELD, SIGNAL, SPEEDX, SPEEDY, STUNNER, TEAMMATES, TOP, WALL };
enum return_values { SUCCESS, ERR_STACK_UNDERFLOW, ERR_NOT_COMPILED, ERR_REG_RANGE, SYNC, ERR_INVALID_JUMP, ERR_INVALID_CHANNEL, ERR_NO_MISSILES };

class Weapon
{
	public:
		Weapon() {}
		~Weapon() {}

		int get_x(void) { return m_x; }
		int get_y(void) { return m_y; }
		int get_damage(void) { return m_damage; }
		bool get_done(void) { return m_done; }
		virtual int hit(int x, int y);
		virtual void step(void)=0;
		virtual void draw(Cairo::RefPtr<Cairo::Context> cr)=0;

	protected:
		int m_damage;
		double m_x;
		double m_y;
		double m_x_step;
		double m_y_step;
		bool m_done;
};

class Rubber_Bullet : public Weapon
{
	public:
		Rubber_Bullet(int x, int y, int angle, int energy);
		void step(void);
		void draw(Cairo::RefPtr<Cairo::Context> cr);
};

class Normal_Bullet : public Weapon
{
	public:
		Normal_Bullet(int x, int y, int angle, int energy);
		void step(void);
		void draw(Cairo::RefPtr<Cairo::Context> cr);
};

class Explosive_Bullet : public Weapon
{
	public:
		Explosive_Bullet(int x, int y, int angle, int energy);
		int hit(int x, int y);
		void step(void);
		void draw(Cairo::RefPtr<Cairo::Context> cr);

	protected:
		bool m_impact;
		double m_radius;
};

class Missile : public Weapon
{
	public:
		Missile(int x, int y, int angle, int energy);
		void step(void);
		void draw(Cairo::RefPtr<Cairo::Context> cr);
};

class WeaponList
{
	public:
		void add_rubber_bullet(int x, int y, int angle, int energy);
		void add_normal_bullet(int x, int y, int angle, int energy);
		void add_explosive_bullet(int x, int y, int angle, int energy);
		void add_missile(int x, int y, int angle, int energy);
		void step(void);
		int hit(int x, int y);
		void draw(Cairo::RefPtr<Cairo::Context> cr);
		void clear(void) { m_weapons.clear(); }

	private:
		std::vector<Weapon*> m_weapons;
};

class RobotState
{
	public:
		RobotState() {}
		~RobotState() {}

		int hardware_points(void);

		// accessors
		// note that some accessors are redundant, all robot registers can be access by the get_reg() and set_reg() methods
		int get_aim(void) { return m_reg[AIM]; }
		int get_bullet_type(void) { return m_bullet_type; }
		void set_bullet_type(int bullet_type) { m_bullet_type = bullet_type; }

		int get_cpu_cycles(void);
		int get_cpu_max(void) { return m_cpu_max; }
		int get_cpu_max_val(void);
		void set_cpu_max(int cpu_max) { m_cpu_max = cpu_max; }

		int get_damage(void) { return m_reg[DAMAGE]; }
		int get_damage_max(void) { return m_damage_max; }
		int get_damage_max_val(void);
		void set_damage_max(int damage_max) { m_damage_max = damage_max; }

		int get_energy(void) { return m_reg[ENERGY]; }
		int get_energy_max(void) { return m_energy_max; }
		int get_energy_max_val(void);
		void set_energy_max(int energy_max) { m_energy_max = energy_max; }

		int get_history(void) { return 0; }

		bool get_hellbores(void) { return m_hellbores; }
		void set_hellbores(bool hellbores) { m_hellbores = hellbores; }

		int get_id(void) { return m_reg[ID]; }
		void set_id(int id) { m_reg[ID] = id; }

		bool get_mines(void) { return m_mines; }
		void set_mines(bool mines) { m_mines = mines; }

		bool get_missiles(void) { return m_missiles; }
		void set_missiles(bool missiles) { m_missiles = missiles; }

		bool get_no_negative_energy(void) { return m_no_negative_energy; }
		void set_no_negative_energy(bool no_negative_energy) { m_no_negative_energy = no_negative_energy; }

		bool get_probes(void) { return m_probes; }
		void set_probes(bool probes) { m_probes = probes; }

		int get_shield(void) { return m_reg[SHIELD]; }
		int get_shield_max(void) { return m_shield_max; }
		int get_shield_max_val(void);
		void set_shield_max(int shield_max) { m_shield_max = shield_max; }

		bool get_stunners(void) { return m_stunners; }
		void set_stunners(bool stunners) { m_stunners = stunners; }

		bool get_tacnukes(void) { return m_tacnukes; }
		void set_tacnukes(bool tacnukes) { m_tacnukes = tacnukes; }

		int get_turret_type(void) { return m_turret_type; }
		void set_turret_type(int turret_type) { m_turret_type = turret_type; }

		int get_x(void) { return m_reg[X]; }
		void set_x(int x) { m_reg[X] = x; }

		int get_y(void) { return m_reg[Y]; }
		void set_y(int y) { m_reg[Y] = y; }

		int get_reg(int reg) { return m_reg[reg]; }

		bool alive(void) { return m_alive; }
		void die(const std::string killer) { m_alive = false; m_killer = killer; }
		std::string get_killer(void) { return m_killer; }

		RobotState state(void);

		bool m_alive;
		std::string m_killer;
		WeaponList m_weapons;

		// hardware store options
		int m_energy_max;
		int m_damage_max;
		int m_shield_max;
		int m_cpu_max;
		int m_bullet_type;
		bool m_missiles;
		bool m_tacnukes;
		bool m_hellbores;
		bool m_mines;
		bool m_stunners;
		bool m_probes;
		int m_turret_type;
		bool m_no_negative_energy;

		// registers
		int m_reg[63];
};

class Robot : public RobotState
{
	public:
		Robot();
		~Robot();

		bool get_modified(void) { return m_modified; }
		void set_modified(bool modified) { m_modified = modified; }

		std::string get_name(void) { return m_name; }
		void set_name(std::string name) { m_name = name; }

		std::string get_path(void) { return m_file_path; }
		void set_path(std::string path) { m_file_path = path; }

		std::string get_src(void) { return m_src; }
		void set_src(std::string src) { m_src = src; }

		bool initialized(void) { return m_initialized; }

		int save(void);
		void open(void);
		std::string compile(void);
		int clock(void);
		int set_reg(int reg, int val);
		void init(void);
		int request_energy(int amount);

	private:
		int parse_labels(std::stringstream *errbuf);
		int full_parse(std::stringstream *errbuf);
		int addlabel(std::string label, int pc, int src_line);
		int getlabel(std::string label);
		int ptoken(std::string token);

		std::string m_name; // robot name
		int m_pc; // program counter
		std::stack<int> m_stack;
		std::string m_file_path; // path to robot file
		std::string m_ltable; // label definitions for compiled code
		std::vector<int> m_compiled; // compiled code storage
		std::string m_src; // source code storage
		bool m_modified; // true if robot has been modified since saving
		std::vector<Label> labels;
		bool m_initialized;
};
